#include "GxString.h"
#include <wchar.h>
#include <string.h>
#include "Windows.h"

#include "Stream.h"
namespace GxLibBasic
{
	BOOL MByteToWChar(LPCSTR lpcszStr, LPWSTR lpwszStr, DWORD dwSize)
	{
		DWORD dwMinSize;
		dwMinSize = MultiByteToWideChar (CP_ACP, 0, lpcszStr, -1, NULL, 0);
		if(dwSize < dwMinSize)
		{
			return FALSE;
		}
		MultiByteToWideChar (CP_ACP, 0, lpcszStr, -1, lpwszStr, dwMinSize);  
		return TRUE;
	}

	BOOL WCharToMByte(LPCWSTR lpcwszStr, LPSTR lpszStr, DWORD dwSize)
	{
		DWORD dwMinSize;
		dwMinSize = WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,NULL,0,NULL,FALSE);
		if(dwSize < dwMinSize)
		{
			return FALSE;
		}
		WideCharToMultiByte(CP_OEMCP,NULL,lpcwszStr,-1,lpszStr,dwSize,NULL,FALSE);
		return TRUE;
	}

	gxStringData::~gxStringData()
	{
		if (Data)
			delete [] Data;
		if (MBChar)
			delete [] MBChar;
	}

	String::String()
	{
		Data = 0;
	}

	String::String(gxStringData *sdata)
	{
		Data = sdata;
	}

	String::String(const wchar_t *str)
	{
		if (str)
		{
			Data = new gxStringData;
			Data->Length = wcslen(str);
			Data->AllocLength = Data->Length + 1;
			Data->RefCount = 1;
			Data->MBChar = 0;
			Data->Data = new wchar_t[Data->AllocLength];
			wcscpy_s(Data->Data,Data->AllocLength,str);
		}
		else
			Data = 0;
	}

	String::String(const String &str)
	{
		Data = str.Data;
		if (Data)
			Data->RefCount ++;
	}

	String::String(const char *str)
	{
		Data = new gxStringData;
		Data->AllocLength = strlen(str)+1;
		Data->Data = new wchar_t[Data->AllocLength];
		Data->MBChar = 0;
		Data->RefCount = 1;
		MByteToWChar(str,Data->Data,Data->AllocLength);
		Data->Length = wcslen(Data->Data);
	}

	String::String(const wchar_t ch)
	{
		Data = new gxStringData;
		Data->Length = 1;
		Data->AllocLength = 2;
		Data->RefCount = 1;
		Data->MBChar = 0;
		Data->Data = new wchar_t[Data->AllocLength];
		Data->Data[0] = ch;
		Data->Data[1] = 0;
	}

	String::String(double val,Int count)
	{
		char buf[512];
		Int dec,sign;
		if (count > 15)
			count = 15;
		_fcvt_s(buf,512,val,count,&dec,&sign);
		this->String::String(static_cast<const char *>(buf));
		if (dec != GetLength())
		{
			if (dec <= 0)
			{
				for (int i=0; i< abs(dec) +1; i++)
				{
					Insert(L"0",0);
				}
				dec = 1;
			}
			Insert(L".",dec);
		}
		if (sign == 1)
		{
			Insert(L"-",0);
		}
		
	}

	String::String(Int val,Int radix)
	{
		wchar_t buf[100];
		_itow_s(val,buf,100,radix);
		this->String::String(static_cast<const wchar_t *>(buf));
	}


	String::~String()
	{
		Dereference();
	}

	void String::Dereference()
	{
		if (Data)
		{
			if (Data->RefCount == 1)
				delete Data;
			else
				Data->RefCount --;
		}
		Data = 0;
	}

	void String::Append(const wchar_t *str)
	{
		if (!str)
			return;
		if (!Data || Data->RefCount > 1)
		{
			Int oriLen = 0;
			wchar_t * oriData;
			if (Data)
			{
				oriLen = Data->Length;
				oriData = Data->Data;
			}
			else
			{
				oriLen = 0;
				oriData = 0;
			}
			gxStringData *pData = new gxStringData;
			pData->Length = oriLen + wcslen(str);
			pData->AllocLength = (int)(pData->Length*1.5)+1;
			pData->Data = new wchar_t[pData->AllocLength];
			if (oriData)
				memcpy(pData->Data,oriData,oriLen * sizeof(wchar_t));
			memcpy(pData->Data+oriLen,str,wcslen(str)*sizeof(wchar_t));
			pData->Data[pData->Length] = 0;
			pData->MBChar = 0;
			pData->RefCount = 1;
			Dereference();
			Data = pData;
		}
		else
		{
			Int nlen = (Data->Length + wcslen(str));
			if (Data->AllocLength < nlen+1)
			{
				Data->AllocLength = (int)(nlen*1.5)+1;
				wchar_t *nstr = new wchar_t[Data->AllocLength];
				memcpy(nstr,Data->Data,Data->Length*sizeof(wchar_t));
				memcpy(nstr+Data->Length,str,wcslen(str)*sizeof(wchar_t));
				memset(nstr+Data->Length+wcslen(str),0,(Data->AllocLength-nlen)*sizeof(wchar_t));
				Data->Length = nlen;
				Data->MBChar = 0;
				delete [] Data->Data;
				Data->Data = nstr;
			}
			else
			{
				Int nlen = wcslen(str);
				memcpy(Data->Data + Data->Length, str,nlen*sizeof(wchar_t));
				Data->Length += nlen;
				Data->Data[Data->Length] = 0;
			}
		}
	}

	void String::Alloc(int len)
	{
		if (Data)
		{
			if (Data->AllocLength < len)
			{
				wchar_t * nstr = new wchar_t[len];
				memcpy(nstr,Data->Data,Data->Length*sizeof(wchar_t));
				Data->Data = nstr;
				Data->AllocLength = len;
			}
		}
	}

	void String::Append(const String &str)
	{
		Append(str.GetStrPtr());
	}

	String & String::Delete(Int idBegin, Int count)
	{
		if (!Data)
			return *this;
		if (idBegin+count>Data->Length)
			count = Data->Length - idBegin;
		if (count<1)
			return *this;
		if (Data->RefCount > 1)
		{
			gxStringData *pData = new gxStringData;
			pData->AllocLength = Data->Length-count+1;
			pData->Data = new wchar_t[pData->AllocLength];
			pData->Length = Data->Length - count;
			pData->MBChar = 0;
			pData->RefCount = 1;
			if (idBegin>0)
				memcpy(pData->Data,Data->Data,idBegin*sizeof(wchar_t));
			memcpy(pData->Data + idBegin,Data->Data + idBegin+count,
				(Data->Length-count-idBegin)*sizeof(wchar_t));
			Dereference();
			Data = pData;
			Data->Data[Data->Length] = 0;
		}
		else
		{
			Data->Length -= count;
			wchar_t * nstr = new wchar_t[Data->Length+1];
			memcpy(nstr,Data->Data,idBegin*sizeof(wchar_t));
			if (Data->Length > idBegin)
				memcpy(nstr+idBegin,Data->Data+idBegin+count,
					(Data->Length - idBegin)*sizeof(wchar_t));
			nstr[Data->Length] = 0;
			delete []Data->Data;
			Data->AllocLength = Data->Length + 1;
			Data->Data = nstr;
		}
		return *this;
	}

	Int String::Find(const wchar_t *str, Int idBegin) const
	{
		wchar_t * rs = wcsstr(Data->Data+idBegin,str);
		if (rs)
			return rs-Data->Data;
		else
			return -1;
	}

	Int String::Find(String &str, Int idBegin) const
	{
		return Find(str.GetStrPtr(),idBegin);
	}

	Int String::GetLength() const
	{
		if (Data)
			return Data->Length;
		else
			return 0;
	}

	const wchar_t * String::GetStrPtr() const
	{
		if (Data)
			return Data->Data;
		else
			return 0;
	}

	/*
	String String::operator +(const wchar_t *str)
	{
		String s(*this);
		s.Append(str);
		return s;
	}
	*/

	String operator +(const String &lhs, const String & rhs)
	{
		String s(lhs);
		s.Append(rhs);
		return s;
	}

	bool String::operator !=(const String &str) const
	{
		return !(*this == str);
	}

	bool String::operator !=(const wchar_t *str) const
	{
		return !(*this == str);
	}

	bool String::operator ==(const String &str) const
	{
		return (*this==str.GetStrPtr());
	}

	bool String::operator ==(const wchar_t *str) const
	{
		if (!Data)
		{
			if (!str || wcslen(str)==0)
				return true;
			else
				return false;
		}
		else
		{
			if (!str)
				if(Data->Length == 0)
					return true;
				else
					return false;

			return (wcscmp(Data->Data,str)==0);
		}
	}

	bool String::operator <(const String &str) const
	{
		return (*this<str.GetStrPtr());
	}

	bool String::operator <(const wchar_t *str) const
	{
		if (!Data)
		{
			if (str && wcslen(str)==0)
				return false;
			else
				return true;
		}
		else
		{
			if (!str)
				return false;
			return (wcscmp(Data->Data,str)<0);
		}
	}

	bool String::operator <=(const String &str) const
	{
		return (*this<=str.GetStrPtr());
	}

	bool String::operator <=(const wchar_t *str) const
	{
		if (!Data)
		{
			return true;
		}
		else
		{
			if (!str)
				return false;
			return (wcscmp(Data->Data,str)<=0);
		}
	}

	bool String::operator >(const String &str) const
	{
		return (*this>str.GetStrPtr());
	}

	bool String::operator >(const wchar_t *str) const
	{
		if (!Data)
		{
			return false;
		}
		else
		{
			if (!str)
				return true;
			return (wcscmp(Data->Data,str)>0);
		}
	}

	bool String::operator >=(const String &str) const
	{
		return (*this>=str.GetStrPtr());
	}

	bool String::operator >=(const wchar_t *str) const
	{
		if (!Data)
		{
			if (str && wcslen(str))
				return false;
			else
				return true;
		}
		else
		{
			if (!str)
				return true;
			return (wcscmp(Data->Data,str)>=0);
		}
	}

	wchar_t & String::operator [](Int x) const
	{
		return Data->Data[x];
	}

	String & String::operator +=(const String &str)
	{
		Append(str);
		return *this;
	}

	String & String::operator +=(const wchar_t *str)
	{
		Append(str);
		return *this;
	}

	String & String::operator +=(const wchar_t c)
	{
		wchar_t str[2];
		str[0] = c;
		str[1] = 0;
		Append(str);
		return *this;
	}

	String & String::operator =(const String &str)
	{
		if (str.Data == Data)
			return *this;
		Dereference();
		this->String::String(str);
		return *this;
	}

	String & String::operator =(const wchar_t *str)
	{
		Dereference();
		this->String::String(String(str));
		return *this;
	}

	void String::Insert(const String &str, Int pos)
	{
		Insert(str.GetStrPtr(),pos);
	}

	void String::Insert(const wchar_t *str, Int pos)
	{
		if (!Data)
			Append(str);
		if (!(pos >= 0 && pos <= Data->Length))
			throw gxIndexOutOfRangeException();
		if (Data->RefCount > 1)
		{
			gxStringData * pData = new gxStringData;
			pData->AllocLength = Data->Length+wcslen(str)+1;
			pData->Length = Data->Length + wcslen(str);
			pData->MBChar = 0;
			pData->RefCount = 1;
			pData->Data = new wchar_t[pData->AllocLength];
			memcpy(pData->Data,Data->Data,pos*sizeof(wchar_t));
			memcpy(pData->Data+pos,str,wcslen(str)*sizeof(wchar_t));
			memcpy(pData->Data+pos+wcslen(str),Data->Data+pos,
				(Data->Length - pos)*sizeof(wchar_t));
			Dereference();
			Data = pData;
			Data->Data[Data->Length] = 0;
		}
		else
		{
			if (Data->AllocLength < Data->Length + (Int)wcslen(str) + 1)
			{
				Data->AllocLength = Data->Length + wcslen(str)*2+1;
				wchar_t *nstr = new wchar_t[Data->AllocLength];
				memset(nstr, 0, Data->AllocLength*sizeof(wchar_t));
				memcpy(nstr,Data->Data,pos*sizeof(wchar_t));
				memcpy(nstr+pos,str,wcslen(str)*sizeof(wchar_t));
				memcpy(nstr+pos+wcslen(str),Data->Data+pos,
					(Data->Length-pos)*sizeof(wchar_t));
				Data->Length += wcslen(str);
				delete [] Data->Data;
				Data->Data = nstr;
				Data->Data[Data->Length] = 0;
			}
			else
			{
				Int slen = wcslen(str);
				for (Int i=Data->Length-1; i>=pos; i--)
				{
					Data->Data[i+slen] = Data->Data[i];
				}
				memcpy(Data->Data + pos,str,sizeof(wchar_t)*slen);
				Data->Length += slen;
				Data->Data[Data->Length] = 0;
			}
		}
	}

	String String::Left(Int x) const
	{
		gxStringData *nData = new gxStringData;
		nData->RefCount = 1;
		nData->AllocLength = x +1;
		nData->Length = x;
		nData->Data = new wchar_t[nData->AllocLength];
		memset(nData->Data,0,sizeof(wchar_t)*nData->AllocLength);
		if (Data && Data->Data)
		{
			memcpy(nData->Data,Data->Data,x*sizeof(wchar_t));
		}
		nData->Data[nData->Length] = 0;
		nData->MBChar = 0;
		String rs(nData);
		return rs;
	}

	String String::Right(Int x) const
	{
		gxStringData *nData = new gxStringData;
		nData->RefCount = 1;
		nData->AllocLength = x +1;
		nData->Length = x;
		nData->Data = new wchar_t[nData->AllocLength];
		memset(nData->Data,0,sizeof(wchar_t)*nData->AllocLength);
		if (Data && Data->Data)
		{
			memcpy(nData->Data,Data->Data+Data->Length -x,x*sizeof(wchar_t));
		}
		nData->Data[nData->Length] = 0;
		nData->MBChar = 0;
		String rs(nData);
		return rs;
	}

	String String::Mid(Int idBegin, Int count) const
	{
		gxStringData *nData = new gxStringData;
		nData->RefCount = 1;
		nData->AllocLength = count +1;
		nData->Length = count;
		nData->Data = new wchar_t[nData->AllocLength];
		memset(nData->Data,0,sizeof(wchar_t)*nData->AllocLength);
		if (Data && Data->Data)
			memcpy(nData->Data,Data->Data+idBegin,count*sizeof(wchar_t));
		nData->Data[nData->Length] = 0;
		nData->MBChar = 0;
		String rs(nData);
		return rs;	
	}

	gxStringData * String::DataModifying()
	{
		gxStringData * oData = Data;
		if (!Data)
			return 0;
		if (Data->RefCount > 1)
		{	
			Data = new gxStringData;
			Data->AllocLength = 0;
			Data->Data = 0;
			Data->Length = 0;
			Data->MBChar = 0;
			Data->RefCount = 1;
			oData->RefCount --;	
		}
		return oData;
	}

	String & String::Lowercase()
	{
		gxStringData *oData = DataModifying();
		if (oData != Data)
		{
			Data->AllocLength = oData->AllocLength;
			Data->Data = new wchar_t[Data->AllocLength];
			Data->MBChar = 0;
			Data->Length = oData->Length;
			Data->RefCount = 1;
			memcpy(Data->Data,oData->Data,oData->AllocLength*sizeof(wchar_t));
		}
		for (Int i=0; i<Data->Length; i++)
		{
			if (Data->Data[i]>=L'A' && Data->Data[i]<=L'Z')
			{
				Data->Data[i] += L'a'-L'A';
			}
		}
		if (!oData->RefCount)
			delete oData;
		return *this;
	}

	String & String::Uppercase()
	{
		gxStringData * oData = DataModifying();
		if (oData != Data)
		{
			Data->AllocLength = oData->AllocLength;
			Data->Data = new wchar_t[Data->AllocLength];
			Data->MBChar = 0;
			Data->Length = oData->Length;
			Data->RefCount = 1;
			memcpy(Data->Data,oData->Data,oData->AllocLength*sizeof(wchar_t));
		}
		for (Int i=0; i<Data->Length; i++)
		{
			if (Data->Data[i]>=L'a' && Data->Data[i]<=L'z')
			{
				Data->Data[i] -= L'a'-L'A';
			}
		}
		if (!oData->RefCount)
			delete oData;
		return *this;
	}

	String & String::LTrim()
	{
		Int sPos = -1;
		gxStringData * oData = DataModifying();
		if (!oData)
			return *this;
		if (oData != Data)
		{
			Data->AllocLength = oData->AllocLength;
			Data->Data = new wchar_t[Data->AllocLength];
			Data->MBChar = 0;
			Data->RefCount = 1;
			Data->Length = oData->Length;
			memcpy(Data->Data,oData->Data,oData->AllocLength*sizeof(wchar_t));
		}
		for (Int i=0; i<Data->Length; i++)
		{
			if (Data->Data[i]!=L' '&&Data->Data[i]!=L'\t'&&Data->Data[i]!=L'\r'&&Data->Data[i]!=L'\n')
			{
				sPos = i;
				break;
			}
		}
		if (sPos == -1)
		{
			Data->Length = 0;
			memset(Data->Data,0,Data->AllocLength*sizeof(wchar_t));
		}
		else if (sPos > 0)
		{
			for (Int i =0; i<Data->Length - sPos; i++)
			{
				Data->Data[i] = Data->Data[i+sPos];
			}
			memset(Data->Data+(Data->Length-sPos),0,(sPos-1)*sizeof(wchar_t));
			Data->Length -= sPos;
			Data->Data[Data->Length] = 0;
		}
		if (!oData->RefCount)
			delete oData;
		return *this;
	}

	String & String::RTrim()
	{
		Int sPos = -1;
		gxStringData * oData = DataModifying();
		if (!oData)
			return *this;
		if (oData != Data)
		{
			Data->AllocLength = oData->AllocLength;
			Data->Data = new wchar_t[Data->AllocLength];
			Data->MBChar = 0;
			Data->Length = oData->Length;
			Data->RefCount = 1;
			memcpy(Data->Data,oData->Data,oData->AllocLength*sizeof(wchar_t));
		}
		for (Int i=Data->Length-1; i>=0; i--)
		{
			if (Data->Data[i]!=L' '&&Data->Data[i]!=L'\t'&&Data->Data[i]!=L'\r'&&Data->Data[i]!=L'\n')
			{
				sPos = i;
				break;
			}
			else
			{
				Data->Data[i] = 0;
			}
		}
		if (sPos == -1)
		{
			Data->Length = 0;
			memset(Data->Data,0,Data->AllocLength*sizeof(wchar_t));
		}
		else
		{
			Data->Length = sPos + 1;
		}
		if (!oData->RefCount)
			delete oData;
		return *this;
	}

	String & Trim(String &str)
	{
		str.RTrim();
		str.LTrim();
		return str;
	}

	const char * String::ToMBString() const
	{
		if (!Data)
			return "";
		if (Data->MBChar)
			delete [] Data->MBChar;
		Data->MBChar = new char[Data->Length*2 +1];
		WCharToMByte(Data->Data,Data->MBChar,Data->Length*2+1);
		return Data->MBChar;
	}

	void String::LoadFromStream(Stream * s)
	{
		int len;
		s->ReadBuffer(&len,1);
		wchar_t *buf = new wchar_t[len];
		s->ReadBuffer(buf,len);
		*this = buf;
		delete [] buf;
	}

	void String::SaveToStream(Stream *s)
	{
		int len = GetLength()+1;
		s->WriteBuffer(&len,1);
		wchar_t *buf = const_cast<wchar_t*>(GetStrPtr());
		if (buf)
		{
			s->WriteBuffer(buf,len);
		}
		else
		{
			buf = new wchar_t[len];
			memset(buf,0,sizeof(wchar_t)*len);
			s->WriteBuffer(buf,len);
			delete [] buf;
		}
	}

	float String::ToFloat() const
	{
		return (float)_wtof(Data->Data); 
	}

	double String::ToDouble() const
	{
		return _wtof(Data->Data); 
	}

	bool IsStringInt(const String &str)
	{
		for (int i=0; i<str.GetLength(); i++)
		{
			if (str[i]>=L'0' && str[i]<=L'9')
				continue;
			else
			{
				if (i == 0 && (str[i] == L'+' || str[i] == L'-'))
					continue;
				else
					return false;
			}
		}
		return true;
	}

	bool IsStringFloat(const String &str)
	{
		bool dot = false;
		String ns = str;
		Trim(ns);
		if (ns == L"+" || ns == L"-")
		{
			return false;
		}
		for (int i=0; i<str.GetLength(); i++)
		{
			if (str[i]>=L'0' && str[i]<=L'9')
				continue;
			else
			{
				if (i == 0 && (str[i] == L'+' || str[i] == L'-'))
					continue;
				else if (!dot && str[i] == L'.')
				{
					dot = true;
					continue;
				}
				else
					return false;
			}
		}
		return true;
	}


	long String::ToLong() const
	{
		return _wtol(Data->Data);
	}

	Int String::ToInt() const
	{
		return _wtoi(Data->Data);
	}

	void SetWidthL(String &str, int w)
	{
		for (int i=str.GetLength(); i<w; i++)
			str = L" " + str;
	}

	void SetWidthR(String &str, int w)
	{
		for (int i=str.GetLength(); i<w; i++)
			str = str + L" ";
	}
}